主要分為兩種 Error:
Operational Errors 運算錯誤
運算錯誤通常是在執行階段發生的錯誤,而非因為系統設計上的邏輯錯誤而產生的 BUG。
舉例來說:
像是伺服器無法連線、資料庫無法連線或是請求超時,等等。
Programming Errors 開發者錯誤
開發者錯誤是開發者在撰寫程式時,應注意而未注意而產生的 BUG,這種情況有可能是因為該 BUG 比較難被察覺或是處理而產生。
舉例來說:
像是讀取了某個 undefined 的屬性、用 "String" 或 "Number" 類型傳入某個應該為 "Object" 類型的參數,等等。
錯誤若發生在同步程式碼中:
根據 Express 官方文件:
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it.
解說:
不需要特別對 middleware 做設計,Express 將捕獲並處理它。
範例:
app.get('/', (req, res) => {
throw new Error('It is broken.') // Express 會自己 catch 它.
});
錯誤若發生在非同步程式碼中:
根據 Express 官方文件:
For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them.
解說:
對於非同步函數傳回的錯誤,必須將它們傳遞給 next() 函數,Express 將在其中擷取並處理它們。
範例:
app.get('/', (req, res, next) => {
fs.readFile('/file-does-not-exist', (err, data) => {
if (err) {
next(err); // 傳遞 errors 給 Express.
} else {
res.send(data);
}
})
})
不過,從 Express 5 開始,傳回 Promise 的 route 處理程序和 middleware 會在拒絕或拋出錯誤時自動呼叫 next(value)。
如以下範例:
app.get('/post/:id', async (req, res, next) => {
const user = await getPostById(req.params.id);
res.send(user);
});
解說:
如果 getPostById 有處理錯誤:
如 throw Error 或 reject,則將使用拋出的錯誤或拒絕的值來呼叫 next 。
如果未提供拒絕值:
會使用 Express 路由器提供的預設錯誤物件來呼叫 next。
Step 1: 建立一個 Custom 的 Error
繼承 Error
類
藉由建立一個 Custom 的 Error,處理 statusCode 跟 Error message
此處建立一個 Custom 的 Error - AppError
class AppError extends Error {
constructor(message, statusCode){
super(message);
this.statusCode = statusCode;
// status code 如果是 4 開頭,就是跟 Client 端互動相關的 Error,也就是 Operational Error
this.status = `${statusCode}`.startWith('4') ? 'fail' : 'error';
this.isOperational = true;
// 返回調用 Stack Trace
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
小百科:
有關 captureStackTrace
可以參考官方文件:
https://nodejs.org/api/errors.html#errors_error_capturestacktrace_targetobject_constructoropt
Step 2: 建立一個 ErrorController
,集中處裡 Errors
首先,我們先建立一個 ErrorController
ErrorController:
module.exports = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
}
在管理 route 的地方 - app.js 加入 ErrorController:
const errorHandler = require('./controllers/errorController');
app.use(errorHandler);
Step 3 - 建立一個控管非同步 Error 的 function
透過 return 調用傳入的 async function,並且針對其 function 做 catch,最後把 error 直接傳給 next。
建立一個 catchAsyncError.js 檔案:
catchAsyncError.js
module.exports = fun => {
return (req, res, next) => {
fun(req, res, next).catch(next);
}
}
今天大致上介紹了幾個 Error handling 會使用到的 function,在後續篇章會舉例說明幾個需要處理的 Error,並實作它 ~ 讓我們明天繼續看下去吧 !
Udemy Node.js, Express, MongoDB & More: The Complete Bootcamp 2023:
https://www.udemy.com/course/nodejs-express-mongodb-bootcamp
Hacksplaining:
https://www.hacksplaining.com/